/**
  ******************************************************************************
  * @file    rs232_Driver.c
  * @author  tuancheng_Zero
  * @version V0.1
  * @date    08/05/2024
  * @brief   rs232 Driver
  ******************************************************************************
*/ 
#include <includes.h>
#include "communi_server.h"

static void RS232_RccInit(void);
static void RS232_GpioInit(void);
static void RS232_DMAInit(void);
static void RS232_NvicInit(void);
static void RS232_UartInit(void);


/// @brief RS232初始化
/// @param  none
void RS232_Init(void){
    // 开时钟
    RS232_RccInit();
    // 配置gpio
    RS232_GpioInit();
    // 配置串口
    RS232_UartInit();
    // 配置DMA
    RS232_DMAInit();
    // 配置中断
    RS232_NvicInit();
}

/// @brief 时钟初始化
/// @param  none
static void RS232_RccInit(void){
    RCC_EnableAPB2PeriphClk(RS232_GPIO_CLK, ENABLE);
    RCC_EnableAPB2PeriphClk(RS232_CLK, ENABLE);
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPH_DMA2, ENABLE);
}

/// @brief 引脚初始化
/// @param  none
static void RS232_GpioInit(void){
    GPIO_InitType GPIO_InitStructure;

    GPIO_InitStructure.Pin         = RS232_RxPin;
    GPIO_InitStructure.GPIO_Speed  = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode   = GPIO_Mode_IN_FLOATING;
    GPIO_InitPeripheral(RS232_GPIO, &GPIO_InitStructure);

    GPIO_InitStructure.Pin         = RS232_TxPin;
    GPIO_InitStructure.GPIO_Speed  = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode   = GPIO_Mode_AF_PP;
    GPIO_InitPeripheral(RS232_GPIO, &GPIO_InitStructure);
}

/// @brief DMA初始化
/// @param  none
static void RS232_DMAInit(void){
    DMA_InitType DMA_InitStructure;
    DMA_DeInit(RS232_Tx_DMA_Channel);
    // 发送通道
    DMA_InitStructure.PeriphAddr     = RS232_DMA_DR_Base;
    DMA_InitStructure.MemAddr        = (uint32_t)RS232TXBufferData;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_DST;
    DMA_InitStructure.BufSize        = 0;
    DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;
    DMA_InitStructure.DMA_MemoryInc  = DMA_MEM_INC_ENABLE;
    DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE;
    DMA_InitStructure.MemDataSize    = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.CircularMode   = DMA_MODE_NORMAL;
    DMA_InitStructure.Priority       = DMA_PRIORITY_VERY_HIGH;
    DMA_InitStructure.Mem2Mem        = DMA_M2M_DISABLE;
    DMA_Init(RS232_Tx_DMA_Channel, &DMA_InitStructure);
    DMA_ConfigInt(RS232_Tx_DMA_Channel, DMA_INT_TXC, ENABLE);    //使能DMA发送完成中断
    DMA_ConfigInt(RS232_Tx_DMA_Channel, DMA_INT_ERR, ENABLE);    //使能DMA异常中断
    USART_EnableDMA(RS232_UART, USART_DMAREQ_TX, ENABLE);        //使能指定UART DMA
    DMA_EnableChannel(RS232_Tx_DMA_Channel, DISABLE);            //禁用指定DMA通道

    // 接收通道
    DMA_DeInit(RS232_Rx_DMA_Channel);
    DMA_InitStructure.PeriphAddr     = RS232_DMA_DR_Base;
    DMA_InitStructure.MemAddr        = (uint32_t)RS232RXBufferData;
    DMA_InitStructure.Direction      = DMA_DIR_PERIPH_SRC;
    DMA_InitStructure.BufSize        = RS232RXBufferDataTOTNum;
    DMA_Init(RS232_Rx_DMA_Channel, &DMA_InitStructure);
    DMA_ConfigInt(RS232_Rx_DMA_Channel, DMA_INT_TXC, ENABLE);      //使能DMA发送完成中断
    DMA_ConfigInt(RS232_Rx_DMA_Channel, DMA_INT_ERR, ENABLE);      //使能DMA异常中断
    USART_EnableDMA(RS232_UART, USART_DMAREQ_RX, ENABLE);          //使能指定UART DMA
    DMA_EnableChannel(RS232_Rx_DMA_Channel, ENABLE);               //使能指定DMA通道
}

/// @brief 中断初始化
/// @param  none
static void RS232_NvicInit(void){
    NVIC_InitType NVIC_InitStructure;

    // Usart 串口中断
    NVIC_InitStructure.NVIC_IRQChannel = RS232_IRQn;         // 选择中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; // 抢占优先级3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;        // 子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           // IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);                           // 根据指定的参数初始化nVIC寄存器
    /* Enable the DMA RX Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = RS232_DMA_Rx_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    /* Enable the DMA TX Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = RS232_DMA_Tx_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/// @brief 串口初始化
/// @param  none
static void RS232_UartInit(void){
    USART_InitType USART_InitStructure;

    USART_InitStructure.BaudRate     = RS232_BaudRate;
    USART_InitStructure.WordLength   = USART_WL_8B;
    USART_InitStructure.StopBits     = USART_STPB_1;
    USART_InitStructure.Parity       = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.Mode         = USART_MODE_RX | USART_MODE_TX;
    USART_Init(RS232_UART, &USART_InitStructure);

    USART_ConfigInt(RS232_UART, USART_INT_IDLEF, ENABLE); // 开启串口空闲中断
    USART_ConfigInt(RS232_UART, USART_INT_ERRF, ENABLE);  // 开启串口溢出中断
    USART_Enable(RS232_UART, ENABLE);
}

/// @brief RS232数据传输函数
/// @param pData 数据缓冲
/// @param length 数据长度
/// @return 
uint8_t RS232_DmaTransferData(uint8_t * pData, uint16_t length){
    uint8_t os_err;

    // 等待数据发送完成
    OSSemPend((OS_EVENT *)App_Sem_RS232TC, 0, &os_err);
    // 配置DMA进行发送
    RS232_Tx_DMA_Channel->MADDR = (uint32_t)pData;
    RS232_Tx_DMA_Channel->TXNUM = length;
    DMA_EnableChannel(RS232_Tx_DMA_Channel, ENABLE);
    return DEF_TRUE;
}

/// @brief 串口中断处理函数(空闲中断)
/// @param  none
void RS232_IRQHandler(void)
{
    uint8_t os_err;
    OS_CPU_SR cpu_sr;
    OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR 		 */
    OSIntEnter();

    /* RX interrupt */
    if (USART_GetIntStatus(RS232_UART, USART_INT_IDLEF) == SET){
        // 清除标志位
        USART_ReceiveData(RS232_UART);
        DMA_EnableChannel(RS232_Rx_DMA_Channel, DISABLE);
        // 通知处理函数数据接收完成
        if(RS232RXBufferData[RS232RXBufferDataTOTNum-1]==COMMRecdataType_Null){
            OSFlagPost(App_Flags_COMMUNI,OSCOMMUNIFlags_RS232RXEnd,OS_FLAG_SET,&os_err);
        }
        // // 开始下一次DMA接收
        // RS232_Rx_DMA_Channel->TXNUM = RS232RXBufferDataTOTNum;
        // DMA_EnableChannel(RS232_Rx_DMA_Channel, ENABLE);
    }
    /* Reset ORE interrupt */
    if (USART_GetIntStatus(RS232_UART, USART_INT_OREF) != RESET){
        USART_ClrIntPendingBit(RS232_UART, USART_INT_OREF);
        // 把数据读走
        USART_ReceiveData(RS232_UART);
    }
    // Check for Noise Error (NE)
    if (USART_GetFlagStatus(RS232_UART, USART_FLAG_NEF) != RESET){
        // Clear Noise Error flag
        USART_ClrFlag(RS232_UART, USART_FLAG_NEF);
        USART_ReceiveData(RS232_UART);
    }
    // Check for Parity Error (FE)
    if (USART_GetFlagStatus(RS232_UART, USART_FLAG_FEF) != RESET){
        // Clear Parity Error flag
        USART_ClrFlag(RS232_UART, USART_FLAG_FEF);
        USART_ReceiveData(RS232_UART);
    }

    OSIntExit(); /* Tell uC/OS-II that we are leaving the ISR 		 */
    OS_EXIT_CRITICAL();
}

/// @brief DMA接收中断函数（一般不会进该中断，溢出时进入）
/// @param  none
void RS232_Rx_DMA_Function(void){
    OS_CPU_SR cpu_sr;
    OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR */
    OSIntEnter();

    DMA_EnableChannel(RS232_Rx_DMA_Channel, DISABLE);          // 禁用DMA
    DMA_ClearFlag(RS232_Rx_DMA_FLAG, RS232_DMA);               // 清标志

    RS232_Rx_DMA_Channel->TXNUM = RS232RXBufferDataTOTNum;        // 从新设定缓冲大小
    DMA_EnableChannel(RS232_Rx_DMA_Channel, ENABLE);           // 处理完,重开DMA

    OSIntExit(); /* Tell uC/OS-II that we are leaving the ISR */
    OS_EXIT_CRITICAL();
}

/// @brief DMA发送中断函数
/// @param  none
void RS232_Tx_DMA_Function(void){
    OS_CPU_SR cpu_sr;
    OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR */
    OSIntEnter();

    DMA_EnableChannel(RS232_Tx_DMA_Channel, DISABLE);      // 关闭DMA
    DMA_ClearFlag(RS232_Tx_DMA_FLAG, RS232_DMA);           // 清标志
    // 释放信号量
    OSSemPost((OS_EVENT *)App_Sem_RS232TC);

    OSIntExit(); /* Tell uC/OS-II that we are leaving the ISR */
    OS_EXIT_CRITICAL();
}
